//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package com.hujun.utils.tree;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;

import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

public class TreeUtils {
    public TreeUtils() {
    }

    public static <T extends Tree<T, K>, K> List<T> toTreeList(List<T> normalList) {
        Predicate<T> getRootCondition = (t) -> {
            K parentId = t.getParentId();
            return Objects.equals(0L, parentId) || Objects.equals("0", parentId);
        };
        return toTreeList(normalList, getRootCondition);
    }

    public static <T extends Tree<T, K>, K> List<T> toTreeList(List<T> normalList, Predicate<T> getRootCondition) {
        if (CollectionUtils.isNotEmpty(normalList)) {
            Map<K, List<T>> parentIdGroupMap = (Map)normalList.stream().collect(Collectors.groupingBy(Tree::getParentId));
            normalList.forEach((t) -> {
                List<T> children = (List)parentIdGroupMap.get(t.getId());
                t.setHasChildren(CollectionUtils.isNotEmpty(children));
                t.setChildren(children);
            });
            return (List)normalList.stream().filter(getRootCondition).collect(Collectors.toList());
        } else {
            return new ArrayList();
        }
    }

    public static <T, K> List<T> toTreeList(List<T> normalList, Function<T, K> getIdFunction, Function<T, K> getParentIdFunction, BiConsumer<T, List<T>> setChildrenFunction, BiConsumer<T, Boolean> setHasChildrenFunction, Predicate<T> getRootCondition) {
        if (CollectionUtils.isNotEmpty(normalList)) {
            Map<K, List<T>> listMap = (Map)normalList.stream().collect(Collectors.groupingBy(getParentIdFunction));
            normalList.forEach((model) -> {
                List<T> children = (List)listMap.get(getIdFunction.apply(model));
                setChildrenFunction.accept(model, children);
                if (CollectionUtils.isNotEmpty(children) && Objects.nonNull(setHasChildrenFunction)) {
                    setHasChildrenFunction.accept(model, true);
                }

            });
            return (List)normalList.stream().filter(getRootCondition).collect(Collectors.toList());
        } else {
            return new ArrayList();
        }
    }

    public static <T> List<T> toNormalList(List<T> treeList, Function<T, List<T>> getChildrenFunction, BiConsumer<T, List<T>> setChildrenFunction, Predicate<T> getRootCondition) {
        List<T> normalList = new ArrayList();
        if (CollectionUtils.isNotEmpty(treeList)) {
            toNormalList(treeList, normalList, getChildrenFunction);
            normalList.forEach((model) -> {
                setChildrenFunction.accept(model, null);
            });
            normalList.addAll((Collection)normalList.stream().filter(getRootCondition).collect(Collectors.toList()));
        }

        return normalList;
    }

    public static <T extends Tree<T, K>, K> List<T> toNormalList(List<T> treeList) {
        return toNormalList(treeList, (t) -> {
            return true;
        });
    }

    public static <T extends Tree<T, K>, K> List<T> toNormalList(List<T> treeList, Predicate<T> getRootCondition) {
        List<T> normalList = new ArrayList();
        if (CollectionUtils.isNotEmpty(treeList)) {
            toNormalList(treeList, (List)normalList);
            (normalList).forEach((t) -> {
                t.setChildren(null);
            });
            normalList = (List)((List)normalList).stream().filter(getRootCondition).collect(Collectors.toList());
        }

        return (List)normalList;
    }

    private static <T extends Tree<T, K>, K> void toNormalList(List<T> sourceList, List<T> targetList) {
        if (CollectionUtils.isNotEmpty(sourceList)) {
            targetList.addAll(sourceList);
            sourceList.forEach((t) -> {
                List<T> children = t.getChildren();
                if (CollectionUtils.isNotEmpty(children)) {
                    toNormalList(children, targetList);
                }

            });
        }

    }

    private static <T> void toNormalList(List<T> sourceList, List<T> targetList, Function<T, List<T>> getChildrenFunction) {
        if (CollectionUtils.isNotEmpty(sourceList)) {
            targetList.addAll(sourceList);
            sourceList.forEach((model) -> {
                List<T> childList = (List)getChildrenFunction.apply(model);
                if (CollectionUtils.isNotEmpty(childList)) {
                    toNormalList(childList, targetList, getChildrenFunction);
                }

            });
        }

    }

    public static <T extends Tree<T, K>, K> List<T> findParentsByBottomId(List<T> normalList, K bottomId) {
        return findParentsByBottomId(normalList, bottomId, ",");
    }

    public static <T extends Tree<T, K>, K> List<T> findParentsByBottomId(List<T> normalList, K bottomId, String delimiter) {
        Objects.requireNonNull(delimiter, "bottomId must not null");
        Objects.requireNonNull(delimiter, "delimiter must not null");
        T node = normalList.stream().filter((t) -> {
            return t.getId().equals(bottomId);
        }).findFirst().orElse(null);
        if (Objects.nonNull(node)) {
            K parentId = node.getParentId();
            String parentIdsStr = node.getParentIds();
            if (StringUtils.isNotBlank(parentIdsStr)) {
                String[] parentIds = parentIdsStr.split(delimiter);
                return (List)normalList.stream().filter((t) -> {
                    return Objects.equals(t.getId(), parentId) || ArrayUtils.contains(parentIds, t.getId());
                }).collect(Collectors.toList());
            }
        }

        return new ArrayList();
    }

    public static <T, K> List<T> findParentsByBottomId(List<T> normalList, K bottomId, Function<T, K> idFunction, Function<T, K> parentIdFunction) {
        return findParentsByBottomId(normalList, bottomId, idFunction, parentIdFunction, (Function)null, (String)null);
    }

    public static <T, K> List<T> findParentsByBottomId(List<T> normalList, K bottomId, Function<T, K> idFunction, Function<T, K> parentIdFunction, Function<T, String> parentIdsFunction, String delimiter) {
        Objects.requireNonNull(idFunction, "idFunction must not null");
        Objects.requireNonNull(parentIdFunction, "parentIdFunction must not null");
        if (CollectionUtils.isEmpty(normalList)) {
            return new ArrayList();
        } else if (!Objects.equals(0L, bottomId) && !Objects.equals("0", bottomId)) {
            T bottom = normalList.stream().filter((t) -> {
                return idFunction.apply(t).equals(bottomId);
            }).findFirst().orElse((null));
            Objects.requireNonNull(bottom, "bottom must not null");
            Map<K, List<T>> idModelMap = (Map)normalList.stream().collect(Collectors.groupingBy(idFunction));
            List<T> parents = new ArrayList();
            parents.add(bottom);
            if (Objects.nonNull(parentIdsFunction)) {
                Objects.requireNonNull(delimiter, "delimiter must not null");
                String parentIdsStr = (String)parentIdsFunction.apply(bottom);
                if (StringUtils.isNotBlank(parentIdsStr)) {
                    String[] parentIds = parentIdsStr.split(delimiter);
                    String[] var11 = parentIds;
                    int var12 = parentIds.length;

                    for(int var13 = 0; var13 < var12; ++var13) {
                        String parentId = var11[var13];
                        List<T> parentList = (List)idModelMap.get(getActualTypeValue(parentId, bottomId.getClass()));
                        if (CollectionUtils.isNotEmpty(parentList)) {
                            parents.addAll(parentList);
                        }
                    }
                }
            } else {
                bottomUp(idModelMap, parents, parentIdFunction.apply(bottom), parentIdFunction);
            }

            return parents;
        } else {
            return new ArrayList();
        }
    }

    public static <T extends Tree<T, K>, K> List<T> findChildrenByTopId(List<T> normalList, K topId) {
        return findChildrenByTopId(normalList, topId, ",");
    }

    public static <T extends Tree<T, K>, K> List<T> findChildrenByTopId(List<T> normalList, K topId, String delimiter) {
        Objects.requireNonNull(delimiter, "delimiter must not null");
        if (!Objects.equals(0L, topId) && !Objects.equals("0", topId)) {
            T node = normalList.stream().filter((t) -> {
                return t.getId().equals(topId);
            }).findFirst().orElse(null);
            return (List)(Objects.nonNull(node) ? (List)normalList.stream().filter((t) -> {
                boolean isChildNode = Objects.equals(topId, t.getParentId());
                if (isChildNode) {
                    return true;
                } else {
                    String parentIdsStr = t.getParentIds();
                    if (StringUtils.isNotBlank(parentIdsStr)) {
                        Objects.requireNonNull(delimiter, "topId must not null");
                        String[] parentIds = parentIdsStr.split(delimiter);
                        return ArrayUtils.contains(parentIds, topId);
                    } else {
                        return false;
                    }
                }
            }).collect(Collectors.toList()) : new ArrayList());
        } else {
            return normalList;
        }
    }

    public static <T, K> List<T> findChildrenByTopId(List<T> normalList, K topId, Function<T, K> idFunction, Function<T, K> parentIdFunction) {
        return findChildrenByTopId(normalList, topId, idFunction, parentIdFunction, (Function)null, (String)null);
    }

    public static <T, K> List<T> findChildrenByTopId(List<T> normalList, K topId, Function<T, K> idFunction, Function<T, K> parentIdFunction, Function<T, String> parentIdsFunction, String delimiter) {
        Objects.requireNonNull(idFunction, "idFunction must not null");
        Objects.requireNonNull(parentIdFunction, "parentIdFunction must not null");
        if (CollectionUtils.isEmpty(normalList)) {
            return new ArrayList();
        } else if (!Objects.equals(0L, topId) && !Objects.equals("0", topId)) {
            T top = normalList.stream().filter((t) -> {
                return idFunction.apply(t).equals(topId);
            }).findFirst().orElse(null);
            Objects.requireNonNull(top, "top must not null");
            Map<K, List<T>> parentIdModelMap = (Map)normalList.stream().collect(Collectors.groupingBy(parentIdFunction));
            List<T> children = new ArrayList();
            children.add(top);
            if (Objects.nonNull(parentIdsFunction)) {
                Objects.requireNonNull(delimiter, "delimiter must not null");
                normalList.forEach((t) -> {
                    String parentIdsStr = (String)parentIdsFunction.apply(t);
                    if (StringUtils.isNotBlank(parentIdsStr)) {
                        String[] parentIds = parentIdsStr.split(delimiter);
                        if (ArrayUtils.contains(parentIds, topId)) {
                            children.add(t);
                        }
                    }

                });
            } else {
                topDown(parentIdModelMap, children, topId, idFunction);
            }

            return children;
        } else {
            return normalList;
        }
    }

    public static <T, K> List<T> findParentsByBottoms(List<T> normalList, List<T> bottomList, Function<T, K> idFunction, Function<T, K> parentIdFunction) {
        if (CollectionUtils.isEmpty(bottomList)) {
            return new ArrayList();
        } else {
            List<T> parents = new ArrayList();
            Iterator var5 = bottomList.iterator();

            while(var5.hasNext()) {
                T bottom = (T) var5.next();
                parents.addAll(findParentsByBottomId(normalList, idFunction.apply(bottom), idFunction, parentIdFunction));
            }

            return parents;
        }
    }

    private static <T, K> void bottomUp(Map<K, List<T>> idModelMap, List<T> parents, K bottomId, Function<T, K> parentIdFunction) {
        List<T> tempList = (List)idModelMap.get(bottomId);
        if (CollectionUtils.isNotEmpty(tempList)) {
            parents.addAll(tempList);
            tempList.forEach((t) -> {
                bottomUp(idModelMap, parents, parentIdFunction.apply(t), parentIdFunction);
            });
        }

    }

    private static <T, K> void topDown(Map<K, List<T>> parentIdModelMap, List<T> children, K topId, Function<T, K> idFunction) {
        List<T> tempList = (List)parentIdModelMap.get(topId);
        if (CollectionUtils.isNotEmpty(tempList)) {
            children.addAll(tempList);
            tempList.forEach((t) -> {
                topDown(parentIdModelMap, children, idFunction.apply(t), idFunction);
            });
        }

    }

    public static boolean isRootNode(String nodeId) {
        return Objects.equals("0", nodeId);
    }

    public static boolean isRootNode(Long nodeId) {
        return Objects.equals(0L, nodeId);
    }

    private static <K> K getActualTypeValue(String value, Class<K> actualType) {
        if (Objects.nonNull(value)) {
            if (Long.class.isAssignableFrom(actualType)) {
                return actualType.cast(Long.valueOf(value));
            } else if (Integer.class.isAssignableFrom(actualType)) {
                return actualType.cast(Integer.valueOf(value));
            } else if (String.class.isAssignableFrom(actualType)) {
                return actualType.cast(value);
            } else if (Boolean.class.isAssignableFrom(actualType)) {
                return actualType.cast(BooleanUtils.toBoolean(value));
            } else if (Double.class.isAssignableFrom(actualType)) {
                return actualType.cast(NumberUtils.toDouble(value));
            } else {
                throw new RuntimeException("不支持的类型");
            }
        } else {
            return null;
        }
    }
}
